#ifndef __OWO_LOG_H__
#define __OWO_LOG_H__

#ifdef owo_logfmt
#warning "owo_logfmt"
#endif

#ifndef owo_log_modulename
#ifdef KBUILD_MODNAME
#define owo_log_modulename              "[" KBUILD_MODNAME "]"
#else
#define owo_log_modulename              ""
#endif
#endif

#ifndef owo_log_prefix
#ifdef KBUILD_MODNAME
#define owo_log_prefix                  "[" KBUILD_MODNAME "]"
#else
#define owo_log_prefix                  ""
#endif
#endif


enum owo_loglevel {
    OWO_NONE    = -2,
    OWO_ALWAYS  = -1,
    OWO_ERR     = 0,
    OWO_WARN,
    OWO_INFO,
    OWO_DBG,
    OWO_DBG2,
    OWO_ALL,
};

enum {
    __LF_CONT,
    __LF_PREFIX,
    __LF_LOGLEVEL,
    __LF_TIMESTAMP,
    __LF_FILE,
    __LF_MODULENAME,
    __LF_FUNCTION,
    __LF_LINE,
    __LF_PARENT,
    __LF_PROCESS,
    __LF_THREAD,
    __LF_MAX,
};

#define owo_logflag(lf)                 (1 << (lf))

#define LF_CONT                         owo_logflag(__LF_CONT)
#define LF_PREFIX                       owo_logflag(__LF_PREFIX)
#define LF_LOGLEVEL                     owo_logflag(__LF_LOGLEVEL)
#define LF_TIMESTAMP                    owo_logflag(__LF_TIMESTAMP)
#define LF_FILE                         owo_logflag(__LF_FILE)
#define LF_MODULENAME                   owo_logflag(__LF_MODULENAME)
#define LF_FUNCTION                     owo_logflag(__LF_FUNCTION)
#define LF_LINE                         owo_logflag(__LF_LINE)
#define LF_PARENT                       owo_logflag(__LF_PARENT)
#define LF_PROCESS                      owo_logflag(__LF_PROCESS)
#define LF_THREAD                       owo_logflag(__LF_THREAD)


#ifndef OWO_LOGLEVEL_DEFAULT
#define OWO_LOGLEVEL_DEFAULT            OWO_INFO
#endif


#ifndef __maybe_unused
#define __maybe_unused                  __attribute__((__unused__))
#endif

#ifndef module_param
#define module_param(...)
#endif

#ifndef MODULE_PARM_DESC
#define MODULE_PARM_DESC(...)
#endif

#ifndef EXPORT_SYMBOL
#define EXPORT_SYMBOL(...)
#endif


/* each module */
#define owo_loglevel_param(name, defval)                \
    int owo_loglevel_##name = defval;                   \
    module_param(owo_loglevel_##name, int, 0664);       \
    MODULE_PARM_DESC(owo_loglevel_##name, "log level for " #name);

/* each file */
#define owo_loglevel_local(name)                        \
    extern int owo_loglevel_##name;                     \
    static int *__owo_loglevel_local __maybe_unused =   \
        &owo_loglevel_##name;

/* share between modules */
#define owo_loglevel_export(name)                       \
    EXPORT_SYMBOL(owo_loglevel_##name);


#define owo_log_level                                   \
    (__builtin_constant_p(__owo_loglevel_local)         \
    ? (*__owo_loglevel_local)                           \
    : OWO_LOGLEVEL_DEFAULT)


#define owo_log_n2str(n)                \
    (n == OWO_ERR    ? "[ERR ]" :       \
    (n == OWO_WARN   ? "[WARN]" :       \
    (n == OWO_INFO   ? "[INFO]" :       \
    (n == OWO_DBG    ? "[DBG ]" :       \
    (n == OWO_DBG2   ? "[DBG2]" :       \
    (n <  0          ? ""       :       \
    "[-]"))))))


/* kernel/include/linux/printk.h */
#ifdef __KERNEL_PRINTK__
#define __owo_log(fmt, ...)                     printk(KERN_CRIT fmt, ##__VA_ARGS__)
#define __log_xxx(flags, match, fmt, ...)       if ((flags) & (match)) {printk(KERN_CONT fmt, ##__VA_ARGS__);}
#define __owo_log_prefix                        KERN_CRIT "" owo_log_prefix
#define owo_log_ts                              ""
#define task_msg(prefix, tsk)                   "[" prefix "%d-%s]", (int)tsk->pid, tsk->comm
#else
#define __owo_log(fmt, ...)                     printf(fmt, ##__VA_ARGS__)
#define __log_xxx(flags, match, fmt, ...)       if ((flags) & (match)) {__owo_log(fmt, ##__VA_ARGS__);}
#define __owo_log_prefix                        owo_log_prefix
#define __owo_log_ts                            0
#define __owo_log_tms                           0
#define owo_log_ts                              "[%2u.%03u]", __owo_log_ts, __owo_log_tms
#define task_msg(prefix, tsk)                   ""
#endif

#define __owo_logf_raw(level, flags, fmt, ...)                                          \
    do {                                                                            \
        if (level <= owo_log_level) {                                               \
            if (!((flags) & LF_CONT)) {                                             \
                __log_xxx(flags, LF_PREFIX,     __owo_log_prefix);                    \
                __log_xxx(flags, LF_LOGLEVEL,   "%s", owo_log_n2str(level));        \
                __log_xxx(flags, LF_TIMESTAMP,  owo_log_ts);                        \
                __log_xxx(flags, LF_FILE,       "[%s]", __FILE__);                  \
                __log_xxx(flags, LF_MODULENAME, owo_log_modulename);                \
                __log_xxx(flags, LF_FUNCTION,   "[%s]", __func__);                  \
                __log_xxx(flags, LF_LINE,       "[L:%d]", __LINE__);                 \
                __log_xxx(flags, LF_PARENT,     task_msg("pp-", current->group_leader->real_parent));   \
                __log_xxx(flags, LF_PROCESS,    task_msg("p-", current->group_leader));                 \
                __log_xxx(flags, LF_THREAD,     task_msg("t-", current));           \
                __log_xxx(flags, owo_logflag(__LF_MAX) - 1,     " ");               \
            }                                                                       \
            __log_xxx(1, 1, fmt, ##__VA_ARGS__);                                    \
        }                                                                           \
    } while (0)


#define __owo_logf(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL, fmt, ##__VA_ARGS__)

#define __owo_logf_ts(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_TIMESTAMP, fmt, ##__VA_ARGS__)

#define __owo_logf_file(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_FILE, fmt, ##__VA_ARGS__)

#define __owo_logf_mod(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_MODULENAME, fmt, ##__VA_ARGS__)

#define __owo_logf_fl(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_FUNCTION | LF_LINE, fmt, ##__VA_ARGS__)

#define __owo_logf_parent(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_PARENT, fmt, ##__VA_ARGS__)

#define __owo_logf_process(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_PROCESS, fmt, ##__VA_ARGS__)

#define __owo_logf_thread(level, flags, fmt, ...) \
    __owo_logf_raw(level, (flags) | LF_PREFIX | LF_LOGLEVEL | LF_THREAD, fmt, ##__VA_ARGS__)

#define __owo_logf_cont(level, flags, fmt, ...) \
    __owo_logf_raw(level, LF_CONT, fmt, ##__VA_ARGS__)

#define __owo_logf_on(level, flags, cond, fmt, ...)                             \
    ({                                                                          \
        int __ret_logn_on = !!(cond);                                          \
        if (__ret_logn_on) {                                                    \
            __owo_logf_fl(level, (flags), "<%s> " fmt, #cond, ##__VA_ARGS__);   \
        }                                                                       \
        __ret_logn_on;                                                          \
    })

#define __owo_logf_on_once(level, flags, cond, fmt, ...)                        \
    ____owo_logf_on_once(__ret_log_once_ ##__LINE__, level, (flags), cond, fmt, ##__VA_ARGS__)

#define ____owo_logf_on_once(var, level, flags, cond, fmt, ...)                 \
    ({                                                                          \
        static int var = 0;                                                     \
        int __ret_logn_on_once = !!(cond);                                      \
        if (__ret_logn_on_once) {                                               \
            if (!var) {                                                         \
                __owo_logf_fl(level, (flags), "<%s> " fmt, #cond, ##__VA_ARGS__);   \
                var = 1;                                                        \
            }                                                                   \
        }                                                                       \
        __ret_logn_on_once;                                                     \
    })

#define owo_logn_raw(level, fmt, ...)               __owo_logf_raw    (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn(level, fmt, ...)                   __owo_logf        (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_ts(level, fmt, ...)                __owo_logf_ts     (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_file(level, fmt, ...)              __owo_logf_file   (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_mod(level, fmt, ...)               __owo_logf_mod    (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_fl(level, fmt, ...)                __owo_logf_fl     (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_parent(level, fmt, ...)            __owo_logf_parent (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_process(level, fmt, ...)           __owo_logf_process(level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_thread(level, fmt, ...)            __owo_logf_thread (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_cont(level, fmt, ...)              __owo_logf_cont   (level, 0, fmt, ##__VA_ARGS__)
#define owo_logn_on(level, cond, fmt, ...)          __owo_logf_on     (level, 0, cond, fmt, ##__VA_ARGS__)
#define owo_logn_on_once(level, cond, fmt, ...)     __owo_logf_on_once(level, 0, cond, fmt, ##__VA_ARGS__)


#define owo_msg_raw(fmt, ...)                       owo_logn_raw    (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg(fmt, ...)                           owo_logn        (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_ts(fmt, ...)                        owo_logn_ts     (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_file(fmt, ...)                      owo_logn_file   (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_mod(fmt, ...)                       owo_logn_mod    (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_fl(fmt, ...)                        owo_logn_fl     (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_parent(fmt, ...)                    owo_logn_parent (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_process(fmt, ...)                   owo_logn_process(OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_thread(fmt, ...)                    owo_logn_thread (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_cont(fmt, ...)                      owo_logn_cont   (OWO_ALWAYS, fmt, ##__VA_ARGS__)
#define owo_msg_on(cond, fmt, ...)                  owo_logn_on     (OWO_ALWAYS, cond, fmt, ##__VA_ARGS__)
#define owo_msg_on_once(cond, fmt, ...)             owo_logn_on_once(OWO_ALWAYS, cond, fmt, ##__VA_ARGS__)


#define owo_err_raw(fmt, ...)                       owo_logn_raw    (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err(fmt, ...)                           owo_logn        (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_ts(fmt, ...)                        owo_logn_ts     (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_file(fmt, ...)                      owo_logn_file   (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_mod(fmt, ...)                       owo_logn_mod    (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_fl(fmt, ...)                        owo_logn_fl     (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_parent(fmt, ...)                    owo_logn_parent (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_process(fmt, ...)                   owo_logn_process(OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_thread(fmt, ...)                    owo_logn_thread (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_cont(fmt, ...)                      owo_logn_cont   (OWO_ERR, fmt, ##__VA_ARGS__)
#define owo_err_on(cond, fmt, ...)                  owo_logn_on     (OWO_ERR, cond, fmt, ##__VA_ARGS__)
#define owo_err_on_once(cond, fmt, ...)             owo_logn_on_once(OWO_ERR, cond, fmt, ##__VA_ARGS__)


#define owo_warn_raw(fmt, ...)                      owo_logn_raw    (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn(fmt, ...)                          owo_logn        (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_ts(fmt, ...)                       owo_logn_ts     (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_file(fmt, ...)                     owo_logn_file   (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_mod(fmt, ...)                      owo_logn_mod    (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_fl(fmt, ...)                       owo_logn_fl     (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_parent(fmt, ...)                   owo_logn_parent (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_process(fmt, ...)                  owo_logn_process(OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_thread(fmt, ...)                   owo_logn_thread (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_cont(fmt, ...)                     owo_logn_cont   (OWO_WARN, fmt, ##__VA_ARGS__)
#define owo_warn_on(cond, fmt, ...)                 owo_logn_on     (OWO_WARN, cond, fmt, ##__VA_ARGS__)
#define owo_warn_on_once(cond, fmt, ...)            owo_logn_on_once(OWO_WARN, cond, fmt, ##__VA_ARGS__)


#define owo_info_raw(fmt, ...)                      owo_logn_raw    (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info(fmt, ...)                          owo_logn        (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_ts(fmt, ...)                       owo_logn_ts     (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_file(fmt, ...)                     owo_logn_file   (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_mod(fmt, ...)                      owo_logn_mod    (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_fl(fmt, ...)                       owo_logn_fl     (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_parent(fmt, ...)                   owo_logn_parent (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_process(fmt, ...)                  owo_logn_process(OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_thread(fmt, ...)                   owo_logn_thread (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_cont(fmt, ...)                     owo_logn_cont   (OWO_INFO, fmt, ##__VA_ARGS__)
#define owo_info_on(cond, fmt, ...)                 owo_logn_on     (OWO_INFO, cond, fmt, ##__VA_ARGS__)
#define owo_info_on_once(cond, fmt, ...)            owo_logn_on_once(OWO_INFO, cond, fmt, ##__VA_ARGS__)


#define owo_dbg_raw(fmt, ...)                       owo_logn_raw    (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg(fmt, ...)                           owo_logn        (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_ts(fmt, ...)                        owo_logn_ts     (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_file(fmt, ...)                      owo_logn_file   (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_mod(fmt, ...)                       owo_logn_mod    (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_fl(fmt, ...)                        owo_logn_fl     (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_parent(fmt, ...)                    owo_logn_parent (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_process(fmt, ...)                   owo_logn_process(OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_thread(fmt, ...)                    owo_logn_thread (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_cont(fmt, ...)                      owo_logn_cont   (OWO_DBG, fmt, ##__VA_ARGS__)
#define owo_dbg_on(cond, fmt, ...)                  owo_logn_on     (OWO_DBG, cond, fmt, ##__VA_ARGS__)
#define owo_dbg_on_once(cond, fmt, ...)             owo_logn_on_once(OWO_DBG, cond, fmt, ##__VA_ARGS__)


#define owo_dbg2_raw(fmt, ...)                      owo_logn_raw    (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2(fmt, ...)                          owo_logn        (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_ts(fmt, ...)                       owo_logn_ts     (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_file(fmt, ...)                     owo_logn_file   (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_mod(fmt, ...)                      owo_logn_mod    (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_fl(fmt, ...)                       owo_logn_fl     (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_parent(fmt, ...)                   owo_logn_parent (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_process(fmt, ...)                  owo_logn_process(OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_thread(fmt, ...)                   owo_logn_thread (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_cont(fmt, ...)                     owo_logn_cont   (OWO_DBG2, fmt, ##__VA_ARGS__)
#define owo_dbg2_on(cond, fmt, ...)                 owo_logn_on     (OWO_DBG2, cond, fmt, ##__VA_ARGS__)
#define owo_dbg2_on_once(cond, fmt, ...)            owo_logn_on_once(OWO_DBG2, cond, fmt, ##__VA_ARGS__)

#endif
